package sessions;

import ejb.facades.AdminFacadeLocal;
import ejb.facades.LoginFacadeLocal;
import ejb.facades.TripFacadeLocal;
import ejb.facades.UserFacadeLocal;
import ejb.facades.VehicleFacadeLocal;
import ejb.facades.extra.UserTripParticipationFacadeLocal;
import ejb.facades.forum.MessageFacadeLocal;
import ejb.facades.forum.PostFacadeLocal;
import ejb.facades.forum.TopicFacadeLocal;
import ejb.facades.location.AddressFacadeLocal;
import ejb.facades.location.CountryFacadeLocal;
import ejb.facades.profiles.DriverProfileFacadeLocal;
import ejb.facades.profiles.PassengerProfileFacadeLocal;
import ejb.facades.profiles.VehicleProfileFacadeLocal;
import ejb.facades.userstate.AwaitingApprovalUserFacadeLocal;
import ejb.facades.userstate.CancelledUserFacadeLocal;
import entities.Admin;
import entities.Login;
import entities.Trip;
import entities.User;
import entities.Vehicle;
import entities.extra.UserTripParticipation;
import entities.forum.Message;
import entities.forum.Post;
import entities.forum.Topic;
import entities.location.Address;
import entities.location.Country;
import entities.profiles.DriverProfile;
import entities.profiles.PassengerProfile;
import entities.profiles.VehicleProfile;
import entities.userstate.AwaitingApprovalUser;
import entities.userstate.CancelledUser;
import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
import java.util.Objects;
import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.enterprise.context.SessionScoped;
import util.Clock;

/**
 * Trabalho PD - 2014/2015
 *
 * @author a21210380 a21210392
 */
@SessionScoped
public class UserAccountSession implements Serializable, IUserAccountSession {

    @EJB
    private Clock clock;
    @EJB
    private AdminFacadeLocal adminFacade;
    @EJB
    private DriverProfileFacadeLocal driverProfileFacade;
    @EJB
    private PassengerProfileFacadeLocal passengerProfileFacade;
    @EJB
    private AddressFacadeLocal addressFacade;
    @EJB
    private CountryFacadeLocal countryFacade;
    @EJB
    private LoginFacadeLocal loginFacade;
    @EJB
    private UserFacadeLocal userFacade;
    @EJB
    private CancelledUserFacadeLocal cancelledUserFacade;
    @EJB
    private VehicleProfileFacadeLocal vehicleProfileFacade;
    @EJB
    private VehicleFacadeLocal vehicleFacade;
    @EJB
    private AwaitingApprovalUserFacadeLocal awaitingApprovalUserFacade;
    @EJB
    private UserTripParticipationFacadeLocal userTripParticipation;
    @EJB
    private TripFacadeLocal tripFacadeLocal;
    @EJB
    private PostFacadeLocal postFacade;
    @EJB
    private TopicFacadeLocal topicFacade;
    @EJB
    private MessageFacadeLocal messageFacade;

    private Integer id;

    @PostConstruct
    public void init() {
        id = null;
    }

    @Override
    public boolean login(String userName, String password) {
        if (id != null) {
            return false;
        }
        if (userName == null || password == null || userName.isEmpty() || password.isEmpty()) {
            return false;
        }

        Login singleResult = loginFacade.findByUserName(userName);
        if (singleResult.doLogin(userName, password)) {
            id = singleResult.getId();
            return true;
        }

        return false;
    }

    @Override
    public boolean logout() {
        if (id == null) {
            return false;
        }
        id = null;
        return true;
    }

    @Override
    public boolean registerUser(String username, String password, String name, int identityNumber, String email, int idCountry, int idAddress, String welcomeMessage, int numberOfSeats, String plateNUmber, String brand, int year) {
        if (id != null) {
            return false;
        }
        if (username == null || username.isEmpty()) {
            return false;
        }
        if (loginFacade.findByUserName(username) != null) {
            return false;
        }
        if (password == null || password.isEmpty()) {
            return false;
        }
        if (name == null || name.isEmpty()) {
            return false;
        }
        if (email == null || email.isEmpty()) {
            return false;
        }
        if (plateNUmber == null || plateNUmber.isEmpty()) {
            return false;
        }
        if (brand == null || brand.isEmpty()) {
            return false;
        }

        Address addressById = addressFacade.find(idAddress);
        if (addressById == null) {
            return false;
        }

        Login login = new Login(username, password, clock.getTime());
        loginFacade.create(login);

        PassengerProfile passengerProfile = new PassengerProfile();
        passengerProfileFacade.create(passengerProfile);

        DriverProfile driverProfile = new DriverProfile();
        driverProfileFacade.create(driverProfile);

        User user = new User(
                login,
                passengerProfile,
                driverProfile,
                addressById,
                name,
                identityNumber,
                email,
                clock.getTime());
        userFacade.create(user);

        VehicleProfile vehicleProfile = new VehicleProfile();
        vehicleProfileFacade.create(vehicleProfile);

        Vehicle vehicle = new Vehicle(vehicleProfile, user, numberOfSeats, plateNUmber, brand, year);
        vehicleFacade.create(vehicle);

        login.setUser(user);
        loginFacade.edit(login);

        user.addVehicle(vehicle);
        userFacade.edit(user);
        vehicleFacade.edit(vehicle);

        awaitingApprovalUserFacade.create(new AwaitingApprovalUser(user, welcomeMessage));

        return true;
    }

    @Override
    public boolean editUser(String password, String name, int identityNumber, String email, int idCountry, int idAddress) {
        if (id == null) {
            return false;
        }
        if (name == null || name.isEmpty()) {
            return false;
        }
        if (email == null || email.isEmpty()) {
            return false;
        }

        Address addressById = addressFacade.find(idAddress);
        if (addressById == null) {
            return false;
        }

        User user = getUser();
        if (user == null) {
            return false;
        }

        user.setName(name);
        user.setBi(identityNumber);
        user.setEmail(email);
        user.setAddress(addressById);

        if (password != null && !password.isEmpty()) {
            user.getLogin().changePassword(password);
        }

        userFacade.edit(user);
        loginFacade.edit(user.getLogin());

        return true;
    }

    @Override
    public boolean editAdmin(String password, String name, int identityNumber, String email) {
        if (id == null) {
            return false;
        }
        if (name == null || name.isEmpty()) {
            return false;
        }
        if (email == null || email.isEmpty()) {
            return false;
        }

        Admin admin = getAdmin();
        if (admin == null) {
            return false;
        }

        admin.setName(name);
        admin.setBi(identityNumber);
        admin.setEmail(email);

        if (password != null && !password.isEmpty()) {
            admin.getLogin().changePassword(password);
        }

        adminFacade.edit(admin);
        loginFacade.edit(admin.getLogin());

        return true;
    }

    @Override
    public Admin getAdmin() {
        if (id != null) {
            return loginFacade.find(id).getAdmin();
        }
        return null;
    }

    @Override
    public User getUser() {
        if (id != null) {
            User user = loginFacade.find(id).getUser();
            if (user != null) {

                AwaitingApprovalUser awaitingApprovalUser = user.getAwaitingApprovalUser();
                if (awaitingApprovalUser != null) {
                    return null;
                }
                CancelledUser cancelledUser = user.getCancelledUser();
                if (cancelledUser != null) {
                    return null;
                }
                user.setLastActionTime(clock.getTime());
                userFacade.edit(user);
            }
            return user;
        }
        return null;
    }

    @Override
    public CancelledUser isCancelled() {
        if (id != null) {
            User user = loginFacade.find(id).getUser();
            if (user != null) {
                return user.getCancelledUser();
            }
        }
        return null;
    }

    @Override
    public AwaitingApprovalUser isWaitingAdminReview() {
        if (id != null) {
            User user = loginFacade.find(id).getUser();
            if (user != null) {
                return user.getAwaitingApprovalUser();
            }
        }
        return null;
    }

    @Override
    public boolean isVisitor() {
        return id == null;
    }

    @Override
    public boolean cancelAccount(String message) {
        if (id == null) {
            return false;
        }
        User user = getUser();
        if (user == null) {
            return false;
        }
        AwaitingApprovalUser awaitingApprovalUser = user.getAwaitingApprovalUser();
        if (awaitingApprovalUser != null) {
            return false;
        }

        CancelledUser cancelledUser = user.getCancelledUser();
        if (cancelledUser != null) {
            return false;
        }

        CancelledUser cancelled = new CancelledUser(user, message, clock.getTime());
        cancelledUserFacade.create(cancelled);

        user.setCancelledUser(cancelled);
        userFacade.edit(user);

        return true;
    }

    @Override
    public boolean activateWaitingAccount(int user_id) {
        if (id == null) {
            return false;
        }

        Admin admin = getAdmin();
        if (admin == null) {
            return false;
        }

        User user = userFacade.find(user_id);
        if (user == null) {
            return false;
        }

        AwaitingApprovalUser awaitingApprovalUser = user.getAwaitingApprovalUser();
        if (awaitingApprovalUser != null) {
            awaitingApprovalUserFacade.remove(awaitingApprovalUser);

            user.approveUser();
            userFacade.edit(user);

            return true;
        }

        return false;
    }

    @Override
    public boolean activateCancelledAccount(int user_id) {
        if (id == null) {
            return false;
        }

        Admin admin = getAdmin();
        if (admin == null) {
            return false;
        }

        User user = userFacade.find(user_id);
        if (user == null) {
            return false;
        }

        CancelledUser cancelledUser = user.getCancelledUser();
        if (cancelledUser != null) {
            cancelledUserFacade.remove(cancelledUser);

            user.activateCancelledAccount();
            userFacade.edit(user);

            return true;
        }

        return false;
    }

    @Override
    public boolean requestCancelledAccountReactivation() {
        if (id == null) {
            return false;
        }
        User user = loginFacade.find(id).getUser();
        if (user == null) {
            return false;
        }

        CancelledUser cancelledUser = user.getCancelledUser();
        if (cancelledUser != null) {
            cancelledUser.unlockAccount();
            cancelledUserFacade.edit(cancelledUser);
            return true;
        }

        return false;
    }

    @Override
    public boolean createTrip(Date departueDate, Date arrivalDate, String description, int id_car, int id_starting_country, int id_starting_address, int id_ending_country, int id_ending_address) {
        if (id == null) {
            return false;
        }
        User user = getUser();
        if (user == null) {
            return false;
        }

        if (departueDate == null || arrivalDate == null) {
            return false;
        }

        if (description == null || description.isEmpty()) {
            return false;
        }

        Vehicle car = null;
        for (Vehicle v : user.getVehicles()) {
            if (v.getId() == id_car) {
                car = v;
                break;
            }
        }
        if (car == null) {
            return false;
        }

        Address startingAddress = addressFacade.find(id_starting_address);
        if (startingAddress == null) {
            return false;
        }

        Address endingAddress = addressFacade.find(id_ending_address);
        if (endingAddress == null) {
            return false;
        }

        Trip trip = new Trip(car, startingAddress, endingAddress, description, departueDate, arrivalDate);
        tripFacadeLocal.create(trip);

        UserTripParticipation tripParticipation = new UserTripParticipation(user, trip, true);
        userTripParticipation.create(tripParticipation);

        user.participateInTrip(tripParticipation);
        userFacade.edit(user);

        trip.addParticipation(tripParticipation);
        tripFacadeLocal.edit(trip);

        return true;
    }

    @Override
    public boolean createAddress(String countryName, String address, String lat, String lng) {
        if (id == null) {
            return false;
        }
        if (countryName == null || countryName.isEmpty()) {
            return false;
        }
        if (address == null || address.isEmpty()) {
            return false;
        }
        if (lat == null || lat.isEmpty()) {
            return false;
        }
        if (lng == null || lng.isEmpty()) {
            return false;
        }

        Admin admin = getAdmin();
        if (admin == null) {
            return false;
        }

        Country findByCountryName = countryFacade.findByCountryName(countryName);
        if (findByCountryName == null) {
            findByCountryName = new Country(countryName);
            countryFacade.create(findByCountryName);
        }

        if (lat.length() > 15) {
            lat = lat.substring(0, 14);
        }
        if (lng.length() > 15) {
            lng = lng.substring(0, 14);
        }

        Address newAddress = new Address(findByCountryName, lat, lng, address);
        addressFacade.create(newAddress);

        findByCountryName.addAddress(newAddress);
        countryFacade.edit(findByCountryName);

        return true;
    }

    @Override
    public int createTopic(String title, String message) {
        if (id == null) {
            return -1;
        }
        if (title == null || title.isEmpty()) {
            return -1;
        }
        if (message == null || message.isEmpty()) {
            return -1;
        }
        User user = getUser();
        if (user == null) {
            return -1;
        }

        Topic topic = new Topic(user, title, message, clock.getTime());
        topicFacade.create(topic);

        user.addTopic(topic);
        userFacade.edit(user);

        return topic.getId();
    }

    @Override
    public int post(int topic_id, String message) {
        if (id == null) {
            return -1;
        }
        if (message == null || message.isEmpty()) {
            return -1;
        }
        User user = getUser();
        if (user == null) {
            return -1;
        }
        Topic topic = topicFacade.find(topic_id);
        if (topic == null) {
            return -1;
        }

        Post p = new Post(user, topic, message, clock.getTime(), topic.getLastInsertedPostNUmber());
        postFacade.create(p);

        topic.incrementInsertedPostNUmber();
        topic.adddPost(p);
        topicFacade.edit(topic);

        user.addPost(p);
        userFacade.edit(user);

        return p.getId();
    }

    @Override
    public int sendEmail(int user_id, String title, String message) {
        if (id == null) {
            return -1;
        }
        if (title == null || title.isEmpty()) {
            return -1;
        }
        if (message == null || message.isEmpty()) {
            return -1;
        }
        User sentUser = getUser();
        if (sentUser == null) {
            return -1;
        }
        User receivingUser = userFacade.find(user_id);
        if (receivingUser == null) {
            return -1;
        }

        if (Objects.equals(sentUser.getId(), receivingUser.getId())) {
            return -1;
        }

        Message m = new Message(receivingUser, sentUser, title, message, clock.getTime());
        messageFacade.create(m);

        sentUser.addSentMessage(m);
        userFacade.edit(sentUser);

        receivingUser.addReceivedMessage(m);
        userFacade.edit(receivingUser);

        return m.getId();
    }

    @Override
    public int createCar(int numberSeats, String plateNumber, String brand, int year) {
        if (id == null) {
            return -1;
        }
        if (plateNumber == null || plateNumber.isEmpty()) {
            return -1;
        }
        if (brand == null || brand.isEmpty()) {
            return -1;
        }

        User user = getUser();
        if (user == null) {
            return -1;
        }

        VehicleProfile vehicleProfile = new VehicleProfile();
        vehicleProfileFacade.create(vehicleProfile);

        Vehicle vehicle = new Vehicle(vehicleProfile, user, numberSeats, plateNumber, brand, year);
        vehicleFacade.create(vehicle);

        user.addVehicle(vehicle);
        userFacade.edit(user);
        vehicleFacade.edit(vehicle);

        return vehicle.getId();
    }

    @Override
    public boolean editCar(int car_id, int numberSeats, String plateNumber, String brand, int year) {
        if (id == null) {
            return false;
        }
        if (plateNumber == null || plateNumber.isEmpty()) {
            return false;
        }
        if (brand == null || brand.isEmpty()) {
            return false;
        }

        User user = getUser();
        if (user == null) {
            return false;
        }

        Vehicle vehicle = vehicleFacade.find(car_id);
        if (vehicle == null) {
            return false;
        }

        if (!Objects.equals(vehicle.getUser().getId(), user.getId())) {
            return false;
        }

        vehicle.setSeats(numberSeats);
        vehicle.setPlateNumber(plateNumber);
        vehicle.setBrand(brand);
        vehicle.setYear(year);
        vehicleFacade.edit(vehicle);

        return true;
    }

    @Override
    public boolean deleteCar(int car_id) {
        if (id == null) {
            return false;
        }
        User user = getUser();
        if (user == null) {
            return false;
        }

        Vehicle vehicle = vehicleFacade.find(car_id);
        if (vehicle == null) {
            return false;
        }

        if (!Objects.equals(vehicle.getUser().getId(), user.getId())) {
            return false;
        }

        vehicleFacade.remove(vehicle);

        user.removeVehicle(vehicle);
        userFacade.edit(user);

        return true;
    }

    @Override
    public boolean evaluate(int id_trip, Map<String, Integer> driver, Map<String, Integer> car, Map<Integer, Map<String, Integer>> passengers) {
        if (id == null) {
            return false;
        }
        User user = getUser();
        if (user == null) {
            return false;
        }
        Trip trip = tripFacadeLocal.find(id_trip);
        if (trip == null) {
            return false;
        }
        if (trip.didUserEvaluatedThisTrip(user)) {
            return false;
        }

        Vehicle vehicle = trip.getVehicle();
        if (!Objects.equals(vehicle.getId(), car.get("id_car"))) {
            return false;
        }
        // se o utilizador logado não é o condutor e não avalia o condutor
        if (driver.isEmpty() && !Objects.equals(vehicle.getUser().getId(), user.getId())) {
            return false;
        }

        Collection<UserTripParticipation> participations = trip.getParticipations();
        if (participations.size() != 2) {
            for (UserTripParticipation utp : participations) {
                Integer temp_id = utp.getUser().getId();

                if (Objects.equals(user.getId(), temp_id)) {
                    continue;
                }

                if (Objects.equals(vehicle.getUser().getId(), temp_id)) {
                    continue;
                }

                // não existe avaliação para o utilizador
                if (!passengers.containsKey(temp_id)) {
                    return false;
                }
            }
        }
        // avaliar carro
        VehicleProfile vp = vehicle.getVehicleProfile();
        vp.evaluate(car.get("cleanliness"), car.get("safety"), car.get("conservation"));
        vehicleProfileFacade.edit(vp);

        if (!driver.isEmpty()) {
            DriverProfile dp = vehicle.getUser().getDriverProfile();
            dp.evaluate(driver.get("courtesy"), driver.get("punctuality"), driver.get("driveCarefully"));
            driverProfileFacade.edit(dp);
        }
        
        if (participations.size() >= 2) {
            for (UserTripParticipation utp : participations) {
                Integer tempId = utp.getUser().getId();
                if (passengers.containsKey(tempId)) {
                    Map<String, Integer> get = passengers.get(tempId);
                    PassengerProfile passengerProfile = utp.getUser().getPassengerProfile();
                    passengerProfile.evaluate(get.get("goodHumored"), get.get("punctuality"));
                    passengerProfileFacade.edit(passengerProfile);
                }
            }
        }

        user.evaluatedTrip(trip);
        userFacade.edit(user);
        trip.userEvaluatedTrip(user);
        tripFacadeLocal.edit(trip);

        return true;
    }

    @Override
    public boolean joinTrip(int id_trip) {
        if (id == null) {
            return false;
        }
        User user = getUser();
        if (user == null) {
            return false;
        }
        Trip trip = tripFacadeLocal.find(id_trip);
        if (trip == null) {
            return false;
        }
        if (trip.userInTrip(user)) {
            return false;
        }

        UserTripParticipation tripParticipation = new UserTripParticipation(user, trip, false);
        userTripParticipation.create(tripParticipation);

        user.participateInTrip(tripParticipation);
        userFacade.edit(user);

        trip.addParticipation(tripParticipation);
        tripFacadeLocal.edit(trip);

        return true;
    }

    @Override
    public boolean acceptUserInTrip(int id_trip, int user_id) {
        if (id == null) {
            return false;
        }
        User driver = getUser();
        if (driver == null) {
            return false;
        }
        User user = userFacade.find(user_id);
        if (user == null) {
            return false;
        }
        Trip trip = tripFacadeLocal.find(id_trip);
        if (trip == null) {
            return false;
        }
        if (trip.userAcceptedInTrip(user)) {
            return false;
        }
        if (!Objects.equals(trip.getVehicle().getUser().getId(), driver.getId())) {
            return false;
        }

        UserTripParticipation participation = trip.getParticipation(user);
        if (participation == null) {
            return false;
        }

        participation.setAccepted(true);
        userTripParticipation.edit(participation);

        return true;
    }
}
